Terraform信者がAWS CDKをはじめて使ってTerraform Backend(S3 + DynamoDB)環境を構築してみた
こんにちは!AWS事業本部コンサルティング部のたかくに(@takakuni_)です。
今回は、Terraform信者である私がAWS CDKを使ってTerraform Backend環境を構築してみようと思います。
AWS CDK触る前と後の印象
AWS CDKを触ってみる前の私の印象は次の通りでした。
- 最終的にCloudFormationスタックでデプロイする仕組みは理解していた
- 「L2, L3コンストラクタが便利!」って聞くけどTerraformの
module
みたいなものかな?
実際使ってみたところ次の印象を率直に思いました。
- 今回は、「L2コンストラクタ」を触ってみたが
module
とはまた違った感覚の「便利すぎ!」を感じた - リソース削除には
removalPolicy
が必要な場合がある - スタックの削除時にスキップされていったからびっくりした
- CloudFormationになかった
autoDeleteObjects
があるのとても良い
作ったコード
実際に作ってみたコードは次のコードになります。
「もっと良い書き方あるよ!」といったフィードバックもいただけると大変嬉しいです。
import { CfnOutput, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'
export class TerraformEnvStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const stateBucket = new s3.Bucket(this, 'stateBucket', {
bucketName: `terraform-state-${this.account}`,
encryption: s3.BucketEncryption.S3_MANAGED,
enforceSSL: true,
versioned: true,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
accessControl: s3.BucketAccessControl.BUCKET_OWNER_FULL_CONTROL,
// 検証のため設定
removalPolicy: RemovalPolicy.DESTROY,
autoDeleteObjects: true
})
const stateLockTable = new dynamodb.Table(this, 'stateLockTable', {
tableName: `terraform-statelock-${this.account}`,
partitionKey: {
name: 'LockID',
type: dynamodb.AttributeType.STRING
},
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
removalPolicy: RemovalPolicy.DESTROY
})
new CfnOutput(this, 'stateBucketName', {
value: stateBucket.bucketName
})
new CfnOutput(this, 'stateLockTableName', {
value: stateLockTable.tableName
})
}
}
触った後の印象を深掘り
先ほど述べた、AWS CDKを触ってみた印象を深掘りしてみます。
L2コンストラクタ
今回、S3バケットをL2コンストラクタで作成した際にとても便利だなと感じたのでご紹介します。
const stateBucket = new s3.Bucket(this, 'stateBucket', {
bucketName: `terraform-state-${this.account}`,
encryption: s3.BucketEncryption.S3_MANAGED,
enforceSSL: true,
versioned: true,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
accessControl: s3.BucketAccessControl.BUCKET_OWNER_FULL_CONTROL,
// 検証のため設定
removalPolicy: RemovalPolicy.DESTROY,
autoDeleteObjects: true
})
enforceSSL
enforceSSL
プロパティは、Security Hubで定義されているAWS Foundational Security Best PracticesのS3.5
をクリアするために使用するプロパティです。
trueまたはfalseで設定し、Terraformに比べて記述量が段違いだったため感動しました。
ちなみにTerraformで記載すると次のような記述になります。
resource "aws_s3_bucket" "stateBucket" {
bucket = "terraform-state-${data.aws_caller_identity.self.account_id}"
}
data "aws_iam_policy_document" "bucket_policy_enforce_ssl" {
version = "2012-10-17"
statement {
effect = "Deny"
actions = [ "s3:*" ]
resources = [
aws_s3_bucket.stateBucket.arn,
"${aws_s3_bucket.stateBucket.arn}/*"
]
condition {
test = "Bool"
variable = "aws:SecureTransport"
values = [ false ]
}
principals {
type = "*"
identifiers = [ "*" ]
}
}
}
resource "aws_s3_bucket_policy" "enforce_ssl" {
bucket = aws_s3_bucket.stateBucket.id
policy = data.aws_iam_policy_document.bucket_policy_enforce_ssl.json
}
removalPolicy
AWS CDKでは、リソースによって削除時や置き換え時のリソースの削除ポリシーが異なります。
今回作成した、S3やDynamoDBテーブルは、削除ポリシーがデフォルトでRETAIN
です。そのため、スタック削除時はリソースがDELETION_SKIPPED
で進行し、初めは仕様に気がつかず困惑しました。
削除ポリシーをDeleteに設定するには、removalPolicy
で制御する必要がありました。
参考
autoDeleteObjects
CloudFormationの場合、スタック削除時にS3バケットが空でない場合、エラーを返します。
よって、次のブログのようなカスタムリソースを使用したオブジェクトの削除処理を組み込むなどの一工夫が必要でした。
Terraformの場合、オブジェクトも削除するプロパティが用意されていたためCloudFormationも対応しないかなと思っていましたが、AWS CDKではサポートされていました。控えめに言って歓喜です。
ドキュメントに記載はありますが、autoDeleteObjects
をtrueにする際は、removalPolicy
で削除ポリシーをDESTROY
にする必要があるみたいです。
const stateBucket = new s3.Bucket(this, 'stateBucket', {
bucketName: `terraform-state-${this.account}`,
encryption: s3.BucketEncryption.S3_MANAGED,
enforceSSL: true,
versioned: true,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
accessControl: s3.BucketAccessControl.BUCKET_OWNER_FULL_CONTROL,
// 検証のため設定
removalPolicy: RemovalPolicy.DESTROY,
autoDeleteObjects: true
})
まとめ
以上、「Terraform信者がAWS CDKをはじめて使ってTerraform Backend(S3 + DynamoDB)環境を構築してみた」でした。
AWS CDKも使えるようになって、IaCおじさんになれるよう頑張っていこうと思います。
以上、AWS事業本部コンサルティング部のたかくに(@takakuni_)でした!